﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyVideoViewerLibrary;
using System.Xml.Linq;
using System.ComponentModel;
using System.Runtime.Remoting.Messaging;
using System.Net;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using System.Text.RegularExpressions;
using System.Runtime.Serialization;

namespace MyVideoViewerManager
{
    public delegate void UpdateEvent(string updateText);
    public delegate void SeriesListEvent(List<Series> seriesList);
    public delegate void SeriesEvent(Series series);

    public enum ImageType
    {
        poster,
        background,
        banner
    }

    class MyVideoViewerManagerBusiness
    {
        public event UpdateEvent DataUpdated;
        public event UpdateEvent DataError;

        private const string SeriesSearchUrl = "http://www.thetvdb.com/api/GetSeries.php?seriesname={0}&language={1}";
        private const string SeriesUrl = "http://www.thetvdb.com/api/{0}/series/{1}/all/{2}.zip";
        public const string GraphicsUrl = "http://images.thetvdb.com/banners/";

        private const string IMDBSearchString = "http://api.themoviedb.org/2.0/Movie.imdbLookup?imdb_id={0}&api_key=4d55ca5fd04590459e2cbd0756c5aad7";
        private const string TitleSearchString = "http://api.themoviedb.org/2.0/Movie.search?title={0}&api_key=4d55ca5fd04590459e2cbd0756c5aad7";
        private const string MovieInfoString = "http://api.themoviedb.org/2.0/Movie.getInfo?id={0}&api_key=4d55ca5fd04590459e2cbd0756c5aad7";

        public const string APIKEY = "4C6A85250D970621";

        public VideoViewer Ctx { get; set; }

        public MyVideoViewerManagerBusiness()
        {
            Ctx = MyVideoViewerBusiness.VideoViewerContext;
        }


        #region TheTVDB

        public List<Language> GetLanguages()
        {
            if (Ctx.Languages.Count() == 0)
            {
                XDocument doc = XDocument.Load(string.Format("http://www.thetvdb.com/api/{0}/languages.xml", APIKEY));

                var q = from l in doc.Descendants("Language")
                        select new Language() { Name = l.Element("name").Value, Abbreviation = l.Element("abbreviation").Value };

                Ctx.Languages.InsertAllOnSubmit(q);
                Ctx.SubmitChanges();
            }

            return Ctx.Languages.OrderBy(l => l.Name).ToList();
        }

        public List<Series> GetSeries()
        {
            return Ctx.Series.OrderBy(s => s.SortName).ToList();
        }

        private delegate List<Series> SearchForSeriesDelegate(string seriesName);
        public List<Series> SearchForSeries(string seriesName)
        {
            XDocument doc = XDocument.Load(string.Format(SeriesSearchUrl, seriesName, Properties.Settings.Default.ChosenLanguage));

            List<Series> seriesList = new List<Series>();

            DateTime tempDate;
            foreach (XElement xseries in doc.Descendants("Series"))
            {
                Series series = new Series()
                {
                    SeriesId = int.Parse(xseries.Element("seriesid").Value),
                    SeriesName = xseries.Element("SeriesName").Value,
                    BannerUrl = xseries.Element("banner") != null ? GraphicsUrl + xseries.Element("banner").Value : "",
                    Overview = xseries.Element("Overview") != null ? xseries.Element("Overview").Value : ""
                };
                if (xseries.Element("FirstAired") != null && DateTime.TryParse(xseries.Element("FirstAired").Value, out tempDate))
                    series.FirstAired = tempDate;

                seriesList.Add(series);
            }

            return seriesList;
        }
        public void SearchForSeriesAsync(string seriesName)
        {
            SearchForSeriesDelegate worker = new SearchForSeriesDelegate(SearchForSeries);
            AsyncCallback completedCallback = new AsyncCallback(SearchForSeriesCallback);

            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            worker.BeginInvoke(seriesName, completedCallback, async);
        }
        private void SearchForSeriesCallback(IAsyncResult ar)
        {
            // get the original worker delegate and the AsyncOperation instance
            SearchForSeriesDelegate worker =
              (SearchForSeriesDelegate)((AsyncResult)ar).AsyncDelegate;
            AsyncOperation async = (AsyncOperation)ar.AsyncState;

            // finish the asynchronous operation
            List<Series> seriesList = worker.EndInvoke(ar);

            // raise the completed event
            async.PostOperationCompleted(
              delegate(object e)
              { OnSearchForSeriesCompleted((List<Series>)e); },
              seriesList);
        }
        public event SeriesListEvent SearchForSeriesCompleted;
        protected virtual void OnSearchForSeriesCompleted(List<Series> e)
        {
            if (SearchForSeriesCompleted != null)
                SearchForSeriesCompleted(e);
        }

        private delegate Series AddSeriesDelegate(int tvSeriesId, string folderLocation);
        public Series AddSeries(int tvSeriesId, string folderLocation)
        {
            Series series = Ctx.Series.SingleOrDefault(s => s.TVSeriesId == tvSeriesId); ;
            if (series == null)
            {
                series = new Series() { TVSeriesId = tvSeriesId };
                //Ctx.Series.InsertOnSubmit(series);
            }

            series.FolderLocation = folderLocation;

            UpdateSeries(ref series);

            return series;
        }
        public void AddSeriesAsync(int tvSeriesId, string folderLocation)
        {
            AddSeriesDelegate worker = new AddSeriesDelegate(AddSeries);
            AsyncCallback completedCallback = new AsyncCallback(AddSeriesCallback);

            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            worker.BeginInvoke(tvSeriesId, folderLocation, completedCallback, async);
        }
        private void AddSeriesCallback(IAsyncResult ar)
        {
            // get the original worker delegate and the AsyncOperation instance
            AddSeriesDelegate worker =
              (AddSeriesDelegate)((AsyncResult)ar).AsyncDelegate;
            AsyncOperation async = (AsyncOperation)ar.AsyncState;

            // finish the asynchronous operation
            Series series = worker.EndInvoke(ar);

            // raise the completed event
            async.PostOperationCompleted(
              delegate(object e)
              { OnAddSeriesCompleted((Series)e); },
              series);
        }
        public event SeriesEvent AddSeriesCompleted;
        protected virtual void OnAddSeriesCompleted(Series e)
        {
            if (AddSeriesCompleted != null)
                AddSeriesCompleted(e);
        }

        private delegate void UpdateSeriesDelegate(ref Series series);
        public void UpdateSeries(ref Series series)
        {
            if (DataUpdated != null)
                DataUpdated("Downloading Series Data");
            try
            {
                string seriesXml = "", bannersXml = "", actorsXml = "";


                WebClient client = new WebClient();
                string downloadUrl = string.Format(SeriesUrl, APIKEY, series.TVSeriesId, Properties.Settings.Default.ChosenLanguage);
                byte[] zipData = client.DownloadData(downloadUrl);
                using (MemoryStream memStream = new MemoryStream(zipData))
                {
                    using (ZipInputStream s = new ZipInputStream(memStream))
                    {
                        ZipEntry zipEntry;

                        while ((zipEntry = s.GetNextEntry()) != null)
                        {
                            using (MemoryStream tempStream = new MemoryStream())
                            {

                                if (!string.IsNullOrEmpty(zipEntry.Name))
                                {
                                    int size = 2048;
                                    byte[] data = new byte[2048];
                                    while ((size = s.Read(data, 0, data.Length)) > 0)
                                        tempStream.Write(data, 0, size);

                                    switch (zipEntry.Name)
                                    {
                                        case "actors.xml":
                                            actorsXml = System.Text.Encoding.UTF8.GetString(tempStream.ToArray());
                                            break;
                                        case "banners.xml":
                                            bannersXml = System.Text.Encoding.UTF8.GetString(tempStream.ToArray());
                                            break;
                                        default:
                                            seriesXml = System.Text.Encoding.UTF8.GetString(tempStream.ToArray());
                                            break;
                                    }
                                }
                            }
                        }

                    }
                }

                if (!string.IsNullOrEmpty(seriesXml))
                {
                    ParseSeriesXml(ref series, seriesXml);
                    if (series != null)
                        ParseEpisodesXml(ref series, seriesXml);

                    if (series != null && !string.IsNullOrEmpty(bannersXml))
                        ParseBannersXml(ref series, bannersXml);

                    if (series != null && !string.IsNullOrEmpty(actorsXml))
                        ParseActorsXml(ref series, actorsXml);
                }



            }
            catch (Exception ex)
            {
                if (DataUpdated != null)
                    DataUpdated("There was an error retrieving Series: " + ex.Message);
            }
        }
        public void UpdateSeriesAsync(ref Series series)
        {
            UpdateSeriesDelegate worker = new UpdateSeriesDelegate(UpdateSeries);
            AsyncCallback completedCallback = new AsyncCallback(UpdateSeriesCallback);

            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            worker.BeginInvoke(ref series, completedCallback, new object[] { series, async });
        }
        private void UpdateSeriesCallback(IAsyncResult ar)
        {
            // get the original worker delegate and the AsyncOperation instance
            UpdateSeriesDelegate worker =
              (UpdateSeriesDelegate)((AsyncResult)ar).AsyncDelegate;
            object[] obj = ar.AsyncState as object[];
            AsyncOperation async = (AsyncOperation)obj[1];

            // finish the asynchronous operation
            Series series = obj[0] as Series;
            worker.EndInvoke(ref series, ar);

            // raise the completed event
            async.PostOperationCompleted(
              delegate(object e)
              { OnUpdateSeriesCompleted(); },
              null);
        }
        public event EventHandler UpdateSeriesCompleted;
        protected virtual void OnUpdateSeriesCompleted()
        {
            if (UpdateSeriesCompleted != null)
                UpdateSeriesCompleted(null, null);
        }

        private void ParseSeriesXml(ref Series series, string xml)
        {
            if (DataUpdated != null)
                DataUpdated("Parsing Series Data");
            try
            {
                XDocument doc = XDocument.Parse(xml);
                XElement xseries = doc.Descendants("Series").FirstOrDefault();
                if (xseries == null)
                    return;

                int seriesId = int.Parse(xseries.Element("id").Value);
                series.TVSeriesId = seriesId;
                //series = ctx.Series.SingleOrDefault(s => s.SeriesId == seriesId);
                if (series.SeriesId == 0)
                {
                    Ctx.Series.InsertOnSubmit(series);
                }

                string temp = "";
                DateTime tempDate;
                decimal tempDecimal;
                int tempInt;

                series.AirsDayOfWeek = xseries.Element("Airs_DayOfWeek") != null ? xseries.Element("Airs_DayOfWeek").Value : "";
                series.AirsTime = xseries.Element("Airs_Time") != null ? xseries.Element("Airs_Time").Value : "";
                if (xseries.Element("FirstAired") != null)
                {
                    if (DateTime.TryParse(xseries.Element("FirstAired").Value, out tempDate))
                        series.FirstAired = tempDate;
                    else
                        series.FirstAired = null;
                }
                series.IMDB_ID = xseries.Element("IMDB_ID") != null ? xseries.Element("IMDB_ID").Value : "";
                series.Overview = xseries.Element("Overview") != null ? xseries.Element("Overview").Value : "";
                if (xseries.Element("Rating") != null)
                {
                    if (decimal.TryParse(xseries.Element("Rating").Value, out tempDecimal))
                        series.Rating = tempDecimal;
                    else
                        series.Rating = null;
                }
                if (xseries.Element("Runtime") != null)
                {
                    if (int.TryParse(xseries.Element("Runtime").Value, out tempInt))
                        series.Runtime = tempInt;
                    else
                        series.Runtime = null;
                }
                //if (xseries.Element("SeriesID") != null)
                //{
                //    if (int.TryParse(xseries.Element("SeriesID").Value, out tempInt))
                //        series.TVSeriesId = tempInt;
                //    else
                //        series.TVSeriesId = null;
                //}
                series.SeriesName = series.SortName = xseries.Element("SeriesName") != null ? xseries.Element("SeriesName").Value.Trim() : "";
                if (series.SortName.ToLower().StartsWith("the"))
                    series.SortName = series.SortName.Substring(4) + ", The";

                if (xseries.Element("lastupdated") != null)
                {
                    if (decimal.TryParse(xseries.Element("lastupdated").Value, out tempDecimal))
                        series.LastUpdated = tempDecimal;
                    else
                        series.LastUpdated = null;
                }

                series.BannerUrl = xseries.Element("banner") != null ? xseries.Element("banner").Value : "";
                series.FanartUrl = xseries.Element("fanart") != null ? xseries.Element("fanart").Value : "";
                series.PosterUrl = xseries.Element("poster") != null ? xseries.Element("poster").Value : "";

                series.Zap2ItId = xseries.Element("zap2it_id") != null ? xseries.Element("zap2it_id").Value : "";

                if (xseries.Element("ContentRating") != null)
                {
                    temp = xseries.Element("ContentRating").Value;
                    TVContentRating contentRating = Ctx.TVContentRatings.SingleOrDefault(r => r.Description == temp);
                    if (contentRating == null)
                    {
                        contentRating = new TVContentRating() { Description = temp };
                        Ctx.TVContentRatings.InsertOnSubmit(contentRating);
                    }
                    contentRating.Series.Add(series);
                }

                // clear out the genres before proceeding, just in case they already exist, so we don't get duplicates
                if (xseries.Element("Genre") != null)
                {
                    temp = xseries.Element("Genre").Value;
                    var split = temp.Split('|').Where(s => !string.IsNullOrEmpty(s));
                    foreach (string temp2 in split)
                    {
                        Genre genre = Ctx.Genres.SingleOrDefault(r => r.GenreDesc == temp2);
                        if (genre == null)
                        {
                            genre = new Genre() { GenreDesc = temp2 };
                            Ctx.Genres.InsertOnSubmit(genre);
                        }

                        Series_Genre seriesGenre = null;
                        if (series.Series_Genres != null)
                            seriesGenre = series.Series_Genres.SingleOrDefault(sg => sg.GenreId == genre.GenreId);
                        if (seriesGenre == null)
                        {
                            seriesGenre = new Series_Genre();
                            series.Series_Genres.Add(seriesGenre);
                            genre.Series_Genres.Add(seriesGenre);
                        }
                    }
                }

                if (xseries.Element("Language") != null)
                {
                    temp = xseries.Element("Language").Value;
                    Language language = Ctx.Languages.SingleOrDefault(l => l.Abbreviation == temp);
                    if (language != null)
                        language.Series.Add(series);
                }

                if (xseries.Element("Network") != null)
                {
                    temp = xseries.Element("Network").Value;
                    Network network = Ctx.Networks.SingleOrDefault(r => r.Name == temp);
                    if (network == null)
                    {
                        network = new Network() { Name = temp };
                        Ctx.Networks.InsertOnSubmit(network);
                    }
                    network.Series.Add(series);
                }

                if (xseries.Element("Status") != null)
                {
                    temp = xseries.Element("Status").Value;
                    TVStatus status = Ctx.TVStatus.SingleOrDefault(r => r.Description == temp);
                    if (status == null)
                    {
                        status = new TVStatus() { Description = temp };
                        Ctx.TVStatus.InsertOnSubmit(status);
                    }
                    status.Series.Add(series);
                }

                Ctx.SubmitChanges();
                // download images

                DirectoryInfo dir = new DirectoryInfo(series.FolderLocation);
                if (!dir.Exists)
                    dir.Create();

                WebClient client = new WebClient();
                string path = Path.Combine(dir.FullName, "banner.jpg");
                if (!File.Exists(path) && !string.IsNullOrEmpty(series.BannerUrl))
                {
                    try
                    {
                        if (DataUpdated != null)
                            DataUpdated("Downloading Banner Image");
                        client.DownloadFile(GraphicsUrl + series.BannerUrl, path);
                    }
                    catch { }
                }
                path = Path.Combine(dir.FullName, "background.jpg");
                if (!File.Exists(path) && !string.IsNullOrEmpty(series.FanartUrl))
                {
                    try
                    {
                        if (DataUpdated != null)
                            DataUpdated("Downloading Fan Art Image");
                        client.DownloadFile(GraphicsUrl + series.FanartUrl, path);
                    }
                    catch { }
                }
                path = Path.Combine(dir.FullName, "folder.jpg");
                if (!File.Exists(path) && !string.IsNullOrEmpty(series.PosterUrl))
                {
                    try
                    {
                        if (DataUpdated != null)
                            DataUpdated("Downloading Poster Image");
                        client.DownloadFile(GraphicsUrl + series.PosterUrl, path);
                    }
                    catch { }
                }

            }
            catch (Exception ex)
            {
                if (DataUpdated != null)
                    DataUpdated("There was an error retrieving series info: " + ex.Message);
            }
        }

        private void ParseEpisodesXml(ref Series series, string xml)
        {
            XDocument doc = XDocument.Parse(xml);

            foreach (XElement xepisode in doc.Descendants("Episode"))
            {
                try
                {

                    int episodeId = int.Parse(xepisode.Element("id").Value);

                    Episode episode = series.Episodes.SingleOrDefault(s => s.TVDBId == episodeId);
                    if (episode == null)
                    {
                        episode = new Episode() { TVDBId = episodeId, DateAdded = DateTime.Now };
                        series.Episodes.Add(episode);
                    }


                    string temp = "";
                    DateTime tempDate;
                    decimal tempDecimal;
                    int tempInt;

                    episode.EpisodeName = xepisode.Element("EpisodeName") != null ? xepisode.Element("EpisodeName").Value : "";

                    if (DataUpdated != null)
                        DataUpdated("Parsing Episode: " + episode.EpisodeName);

                    if (xepisode.Element("EpisodeNumber") != null)
                    {
                        if (int.TryParse(xepisode.Element("EpisodeNumber").Value, out tempInt))
                            episode.EpisodeNumber = tempInt;
                        else
                            episode.EpisodeNumber = null;
                    }

                    if (xepisode.Element("SeasonNumber") != null)
                    {
                        if (int.TryParse(xepisode.Element("SeasonNumber").Value, out tempInt))
                            episode.SeasonNumber = tempInt;
                        else
                            episode.SeasonNumber = null;
                    }
                    if (xepisode.Element("seasonid") != null)
                    {
                        if (int.TryParse(xepisode.Element("seasonid").Value, out tempInt))
                        {
                            Season season = series.Seasons.SingleOrDefault(s => s.TVDBId == tempInt);
                            if (season == null)
                            {
                                season = new Season() { TVDBId = tempInt };
                                //Ctx.Seasons.InsertOnSubmit(season);
                            }
                            if (episode.SeasonNumber.HasValue)
                                season.SeasonNumber = episode.SeasonNumber.Value;
                            season.Episodes.Add(episode);
                            series.Seasons.Add(season);
                        }
                    }

                    if (xepisode.Element("FirstAired") != null)
                    {
                        if (DateTime.TryParse(xepisode.Element("FirstAired").Value, out tempDate))
                            episode.FirstAired = tempDate;
                        else
                            episode.FirstAired = null;
                    }

                    //Ctx.Episode_Person.DeleteAllOnSubmit(Ctx.Episode_Person.Where(g => g.EpisodeId == episodeId));
                    if (xepisode.Element("GuestStars") != null)
                    {
                        temp = xepisode.Element("GuestStars").Value;
                        List<string> split = new List<string>();
                        if (temp.Contains('|'))
                            split.AddRange(temp.Split('|').Where(s => !string.IsNullOrEmpty(s)));
                        else
                            split.AddRange(temp.Split(',').Where(s => !string.IsNullOrEmpty(s)));
                        foreach (string temp2 in split.Distinct())
                        {
                            Person person = Ctx.Persons.SingleOrDefault(r => r.PersonName == temp2.Trim());
                            if (person == null)
                            {
                                person = new Person() { PersonName = temp2.Trim() };
                                Ctx.Persons.InsertOnSubmit(person);
                            }

                            Episode_Person episodePerson = episode.Episode_Persons.SingleOrDefault(ep => ep.PersonId == person.PersonId);
                            if (episodePerson == null)
                            {
                                episodePerson = new Episode_Person();
                                episode.Episode_Persons.Add(episodePerson);
                                person.Episode_Persons.Add(episodePerson);
                            }
                        }
                    }

                    episode.IMDB_ID = xepisode.Element("IMDB_ID") != null ? xepisode.Element("IMDB_ID").Value : "";

                    if (xepisode.Element("Language") != null)
                    {
                        temp = xepisode.Element("Language").Value;
                        Language language = Ctx.Languages.SingleOrDefault(l => l.Abbreviation == temp);
                        if (language != null)
                            language.Episodes.Add(episode);
                    }

                    episode.Overview = xepisode.Element("Overview") != null ? xepisode.Element("Overview").Value : "";

                    if (xepisode.Element("Rating") != null)
                    {
                        if (decimal.TryParse(xepisode.Element("Rating").Value, out tempDecimal))
                            episode.Rating = tempDecimal;
                        else
                            episode.Rating = null;
                    }

                    episode.ProductionCode = xepisode.Element("ProductionCode") != null ? xepisode.Element("ProductionCode").Value : "";
                    episode.ImageUrl = xepisode.Element("filename") != null ? xepisode.Element("filename").Value : "";


                    Ctx.SubmitChanges();
                }
                catch (Exception ex)
                {
                    if (DataUpdated != null)
                        DataUpdated("There was an error retrieving the episode: " + ex.Message);
                }
            }
        }

        private void ParseBannersXml(ref Series series, string xml)
        {
            int seriesId = series.SeriesId;
            //ctx.Banners.DeleteAllOnSubmit(ctx.Banners.Where(b => b.SeriesId == seriesId));
            if (DataUpdated != null)
                DataUpdated("Parsing Banner Data");

            XDocument doc = XDocument.Parse(xml);

            string temp;
            int tempInt;

            foreach (XElement xbanner in doc.Descendants("Banner"))
            {
                try
                {
                    int bannerId = int.Parse(xbanner.Element("id").Value);

                    if (DataUpdated != null)
                        DataUpdated("Importing Banner " + bannerId.ToString());

                    Banner banner = series.Banners.SingleOrDefault(b => b.TVDBId == bannerId);
                    if (banner == null)
                    {
                        banner = new Banner() { TVDBId = bannerId };
                        //Ctx.Banners.InsertOnSubmit(banner);
                    }
                    series.Banners.Add(banner);

                    banner.BannerPath = xbanner.Element("BannerPath") != null ? xbanner.Element("BannerPath").Value : "";
                    banner.BannerType = xbanner.Element("BannerType") != null ? xbanner.Element("BannerType").Value : "";
                    banner.BannerType2 = xbanner.Element("BannerType2") != null ? xbanner.Element("BannerType2").Value : "";
                    banner.ThumbnailPath = xbanner.Element("ThumbnailPath") != null ? xbanner.Element("ThumbnailPath").Value : "";
                    banner.VignettePath = xbanner.Element("VignettePath") != null ? xbanner.Element("VignettePath").Value : "";

                    if (xbanner.Element("Season") != null)
                    {
                        if (int.TryParse(xbanner.Element("Season").Value, out tempInt))
                            banner.Season = tempInt;
                        else
                            banner.Season = null;
                    }

                    Ctx.Banner_Colors.DeleteAllOnSubmit(Ctx.Banner_Colors.Where(g => g.BannerId == bannerId));
                    if (xbanner.Element("Colors") != null)
                    {
                        temp = xbanner.Element("Colors").Value;
                        var split = temp.Split('|').Where(s => !string.IsNullOrEmpty(s));
                        foreach (string temp2 in split)
                        {
                            Color color = Ctx.Colors.SingleOrDefault(r => r.ColorValue == temp2);
                            if (color == null)
                            {
                                color = new Color() { ColorValue = temp2 };

                                string[] split2 = temp2.Split(',');
                                if (split2.Length == 3)
                                {
                                    if (int.TryParse(split2[0], out tempInt))
                                        color.Red = tempInt;
                                    if (int.TryParse(split2[1], out tempInt))
                                        color.Green = tempInt;
                                    if (int.TryParse(split2[2], out tempInt))
                                        color.Blue = tempInt;
                                }

                                Ctx.Colors.InsertOnSubmit(color);
                            }

                            Banner_Color bannerColor = banner.Banner_Colors.SingleOrDefault(bc => bc.ColorId == color.ColorId);
                            if (bannerColor == null)
                            {
                                bannerColor = new Banner_Color();
                                color.Banner_Colors.Add(bannerColor);
                                banner.Banner_Colors.Add(bannerColor);
                            }
                        }
                    }

                    if (xbanner.Element("Language") != null)
                    {
                        temp = xbanner.Element("Language").Value;
                        Language language = Ctx.Languages.SingleOrDefault(l => l.Abbreviation == temp);
                        if (language != null)
                            language.Banners.Add(banner);
                    }

                    Ctx.SubmitChanges();
                }
                catch (Exception ex)
                {
                    if (DataUpdated != null)
                        DataUpdated("There was an error retrieving the banner: " + ex.Message);
                }
            }
        }

        private void ParseActorsXml(ref Series series, string xml)
        {
            if (DataUpdated != null)
                DataUpdated("Parsing Actor Data");

            int seriesId = series.SeriesId;
            //Ctx.Series_Person.DeleteAllOnSubmit(series.Series_Person);

            XDocument doc = XDocument.Parse(xml);

            string temp;
            int tempInt;
            WebClient client = new WebClient();

            foreach (XElement xActor in doc.Descendants("Actor"))
            {
                try
                {
                    int actorId = int.Parse(xActor.Element("id").Value);
                    Series_Person actor = series.Series_Persons.SingleOrDefault(b => b.TVDBId == actorId);
                    if (actor == null)
                    {
                        actor = new Series_Person() { TVDBId = actorId };
                        //Ctx.Series_Person.InsertOnSubmit(actor);
                    }

                    temp = xActor.Element("Name").Value;

                    if (DataUpdated != null)
                        DataUpdated("Importing Actor: " + temp);

                    Person person = Ctx.Persons.SingleOrDefault(p => p.PersonName == temp);
                    if (person == null)
                    {
                        person = new Person() { PersonName = temp };
                        Ctx.Persons.InsertOnSubmit(person);
                    }

                    actor.Image = xActor.Element("Image") != null ? xActor.Element("Image").Value : "";
                    actor.Role = xActor.Element("Role") != null ? xActor.Element("Role").Value : "";

                    if (xActor.Element("SortOrder") != null)
                    {
                        if (int.TryParse(xActor.Element("SortOrder").Value, out tempInt))
                            actor.SortOrder = tempInt;
                        else
                            actor.SortOrder = null;
                    }

                    person.Series_Persons.Add(actor);
                    series.Series_Persons.Add(actor);



                    if (!string.IsNullOrEmpty(actor.Image))
                    {

                        DirectoryInfo actorsDir = new DirectoryInfo(Path.Combine(series.FolderLocation, "actors"));
                        if (!actorsDir.Exists)
                            actorsDir.Create();

                        try
                        {
                            temp = Path.GetFileName(actor.Image);
                            string imageLocation = Path.Combine(actorsDir.FullName, temp);

                            if (!File.Exists(imageLocation))
                            {
                                if (DataUpdated != null)
                                    DataUpdated("Downloading Actor Image");
                                client.DownloadFile(GraphicsUrl + actor.Image, imageLocation);

                            }
                            actor.ImageLocation = imageLocation;
                        }
                        catch { }
                    }

                    Ctx.SubmitChanges();

                }
                catch (Exception ex)
                {
                    if (DataUpdated != null)
                        DataUpdated("There was an error retrieving the actor: " + ex.Message);
                }
            }
        }

        public List<Season> GetWatchedSeasons()
        {
            var q = (from s in Ctx.Seasons
                     where s.WatchSeason
                     orderby s.Series.SeriesName, s.SeasonNumber
                     select s).ToList();
            return q.ToList();
        }

        private delegate void UpdateEpisodeFilesDelegate(ref Series series);
        public void UpdateEpisodeFiles(ref Series series)
        {
            DirectoryInfo dir = new DirectoryInfo(series.FolderLocation);
            if (!dir.Exists)
                return;

            foreach (DirectoryInfo seasonDir in dir.GetDirectories("Season*"))
            {
                Season season = null;
                AddFilesToSeries(seasonDir.GetFiles().Select(f => f.FullName), ref series, ref season);
            }
        }
        public void UpdateEpisodeFilesAsync(ref Series series)
        {
            UpdateEpisodeFilesDelegate worker = new UpdateEpisodeFilesDelegate(UpdateEpisodeFiles);
            AsyncCallback completedCallback = new AsyncCallback(UpdateEpisodeFilesCallback);

            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            worker.BeginInvoke(ref series, completedCallback, new object[] { series, async });
        }
        private void UpdateEpisodeFilesCallback(IAsyncResult ar)
        {
            // get the original worker delegate and the AsyncOperation instance
            UpdateEpisodeFilesDelegate worker =
              (UpdateEpisodeFilesDelegate)((AsyncResult)ar).AsyncDelegate;
            object[] obj = ar.AsyncState as object[];
            AsyncOperation async = (AsyncOperation)obj[1];

            // finish the asynchronous operation
            Series series = obj[0] as Series;
            worker.EndInvoke(ref series, ar);

            // raise the completed event
            //AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, false, episdoe);
            async.PostOperationCompleted(
              delegate(object e)
              { OnUpdateEpisodeFilesCompleted(); },
              null);
        }
        public event EventHandler UpdateEpisodeFilesCompleted;
        protected virtual void OnUpdateEpisodeFilesCompleted()
        {
            if (UpdateEpisodeFilesCompleted != null)
                UpdateEpisodeFilesCompleted(this, null);
        }

        private delegate void AddFilesToSeriesDelegate(IEnumerable<string> filePaths, ref Series series, ref Season season);
        public void AddFilesToSeries(IEnumerable<string> filePaths, ref Series series, ref Season season)
        {
            string[] regEx = new string[] { "s(\\d+)e(\\d+)", @"\b(\d+)" };

            int seriesId = series.SeriesId;

            foreach (string filePath in filePaths)
            {

                try
                {
                    FileInfo file = new FileInfo(filePath);

                    int seasonNum = -1, episodeNum = -1;
                    Regex regex = new Regex(regEx[0], RegexOptions.IgnoreCase);
                    if (regex.IsMatch(file.Name))
                    {
                        Match match = regex.Match(file.Name);
                        seasonNum = int.Parse(match.Groups[1].Value);
                        episodeNum = int.Parse(match.Groups[2].Value);
                    }
                    else
                    {
                        regex = new Regex(regEx[1], RegexOptions.IgnoreCase);
                        if (regex.IsMatch(file.Name))
                        {
                            Match match = regex.Match(file.Name);
                            episodeNum = int.Parse(match.Groups[1].Value);

                            regex = new Regex("season (\\d+)", RegexOptions.IgnoreCase);
                            if (regex.IsMatch(file.Directory.Name))
                            {
                                Match match2 = regex.Match(file.Directory.Name);
                                seasonNum = int.Parse(match2.Groups[1].Value);
                            }
                            else if (season != null)
                                seasonNum = season.SeasonNumber;
                        }
                    }
                    if (episodeNum > -1 && seasonNum > -1 && series.Episodes != null)
                    {

                        Episode episode = series.Episodes.SingleOrDefault(e => e.EpisodeNumber == episodeNum && e.SeasonNumber == seasonNum);
                        if (episode != null)
                            AddFileToSeries(ref episode, filePath);
                    }

                }
                catch (Exception ex)
                {
                    string message = ex.Message;
                    if (DataUpdated != null)
                        DataUpdated("Could not import file");
                }
            }

        }
        public void AddFilesToSeriesAsync(IEnumerable<string> filePaths, ref Series series, ref Season season)
        {
            AddFilesToSeriesDelegate worker = new AddFilesToSeriesDelegate(AddFilesToSeries);
            AsyncCallback completedCallback = new AsyncCallback(AddFilesToSeriesCallback);

            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            worker.BeginInvoke(filePaths, ref series, ref season, completedCallback, new object[] { series, season, async });
        }
        private void AddFilesToSeriesCallback(IAsyncResult ar)
        {
            // get the original worker delegate and the AsyncOperation instance
            AddFilesToSeriesDelegate worker =
              (AddFilesToSeriesDelegate)((AsyncResult)ar).AsyncDelegate;
            object[] obj = ar.AsyncState as object[];
            AsyncOperation async = (AsyncOperation)obj[2];

            // finish the asynchronous operation
            Series series = obj[0] as Series;
            Season season = obj[1] as Season;
            worker.EndInvoke(ref series, ref season, ar);

            // raise the completed event
            //AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, false, episdoe);
            async.PostOperationCompleted(
              delegate(object e)
              { OnAddFilesToSeriesCompleted(); },
              null);
        }
        public event EventHandler AddFilesToSeriesCompleted;
        protected virtual void OnAddFilesToSeriesCompleted()
        {
            if (AddFilesToSeriesCompleted != null)
                AddFilesToSeriesCompleted(this, null);
        }

        private delegate void AddFileToSeriesDelegate(ref Episode episode, string filePath);
        public void AddFileToSeries(ref Episode _episode, string filePath)
        {
            if (DataUpdated != null)
                DataUpdated("Importing File: " + filePath);
            string[] regEx = new string[] { "s(\\d+)e(\\d+)", @"\b(\d+)" };

            Episode episode = _episode;

            bool moveFile = Properties.Settings.Default.MoveFile;
            try
            {
                //Episode episode = Ctx.Episodes.SingleOrDefault(e => e.EpisodeId == episodeId);
                if (episode != null)
                {
                    FileInfo file = new FileInfo(filePath);
                    if (DataUpdated != null)
                        DataUpdated("Adding file to episode: " + episode.EpisodeName);
                    string targetFile = file.Name;
                    if (Properties.Settings.Default.RenameFile)
                    {
                        moveFile = true;
                        targetFile = string.Format(Properties.Settings.Default.RenameFormat + "{4}", episode.Series.SeriesName, episode.SeasonNumber, episode.EpisodeName, episode.EpisodeNumber, file.Extension);
                    }
                    targetFile = Regex.Replace(targetFile, @"[?:\/*""<>|]", "");
                    targetFile = Path.Combine(Path.Combine(episode.Series.FolderLocation, "Season " + episode.SeasonNumber.Value.ToString()), targetFile);

                    DirectoryInfo targetDirectory = new DirectoryInfo(Path.GetDirectoryName(targetFile));
                    if (!targetDirectory.Exists)
                        targetDirectory.Create();

                    WebClient client = new WebClient();
                    string folderPoster = Path.Combine(targetDirectory.FullName, "folder.jpg");
                    if (!File.Exists(folderPoster))
                    {

                        Banner banner = Ctx.Banners.FirstOrDefault(b => b.SeriesId == episode.SeriesId && b.Season.HasValue && b.Season.Value == episode.SeasonNumber.Value && b.LanguageId == episode.LanguageId);
                        if (banner != null)
                        {
                            if (DataUpdated != null)
                                DataUpdated("Dowloading Folder Poster");

                            client.DownloadFile(Path.Combine(GraphicsUrl, banner.BannerPath), folderPoster);
                        }
                    }

                    DirectoryInfo metaDataFolder = new DirectoryInfo(Path.Combine(targetDirectory.FullName, "metadata"));
                    if (!metaDataFolder.Exists)
                        metaDataFolder.Create();

                    string episodeImage = Path.Combine(metaDataFolder.FullName, Path.GetFileNameWithoutExtension(targetFile) + ".jpg");
                    if (!File.Exists(episodeImage) && !string.IsNullOrEmpty(episode.ImageUrl))
                    {
                        if (DataUpdated != null)
                            DataUpdated("Dowloading Episode Poster: " + episode.EpisodeNameAndNumber);

                        try
                        {
                            client.DownloadFile(GraphicsUrl + episode.ImageUrl, episodeImage);
                            episode.ImageFileLocation = episodeImage;
                        }
                        catch { }
                    }
                    else
                        episode.ImageFileLocation = episodeImage;

                    if (file.FullName != targetFile)
                    {
                        if (moveFile)
                        {
                            if (DataUpdated != null)
                                DataUpdated("Moving File for Episode: " + episode.EpisodeNameAndNumber);
                            file.MoveTo(targetFile);
                        }
                        else
                        {
                            if (DataUpdated != null)
                                DataUpdated("Copying File for Episode: " + episode.EpisodeNameAndNumber);
                            file.CopyTo(targetFile, true);
                        }
                    }
                    episode.FileLocation = targetFile;
                    Ctx.SubmitChanges();
                }
            }
            catch (Exception ex)
            {
                if (DataError != null)
                    DataError(ex.Message);
            }
        }
        public void AddFileToSeriesAsync(ref Episode episode, string filePath)
        {
            AddFileToSeriesDelegate worker = new AddFileToSeriesDelegate(AddFileToSeries);
            AsyncCallback completedCallback = new AsyncCallback(AddFileToSeriesCallback);

            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            worker.BeginInvoke(ref episode, filePath, completedCallback, new object[] { episode, async });
        }
        private void AddFileToSeriesCallback(IAsyncResult ar)
        {
            // get the original worker delegate and the AsyncOperation instance
            AddFileToSeriesDelegate worker =
              (AddFileToSeriesDelegate)((AsyncResult)ar).AsyncDelegate;
            object[] obj = ar.AsyncState as object[];
            AsyncOperation async = (AsyncOperation)obj[1];

            // finish the asynchronous operation
            Episode episode = obj[0] as Episode;
            worker.EndInvoke(ref episode, ar);

            // raise the completed event
            //AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, false, episdoe);
            async.PostOperationCompleted(
              delegate(object e)
              { OnAddFileToSeriesCompleted(); },
              null);
        }
        public event EventHandler AddFileToSeriesCompleted;
        protected virtual void OnAddFileToSeriesCompleted()
        {
            if (AddFileToSeriesCompleted != null)
                AddFileToSeriesCompleted(this, null);
        }
        
        #endregion

        #region theMovieDb

        public List<Movie> SearchByIMDB(string imdb)
        {
            if (string.IsNullOrEmpty(imdb))
                return new List<Movie>();
            XDocument xdoc = XDocument.Load(string.Format(IMDBSearchString, imdb));
            return ParseSearch(ref xdoc);
        }

        public List<Movie> SearchByTitle(string title)
        {
            if (string.IsNullOrEmpty(title))
                return new List<Movie>();
            XDocument xdoc = XDocument.Load(string.Format(TitleSearchString, title));
            return ParseSearch(ref xdoc);
        }

        private List<Movie> ParseSearch(ref XDocument xdoc)
        {
            List<Movie> movies = new List<Movie>();
            foreach (XElement xMovie in xdoc.Descendants("movie"))
            {
                try
                {
                    Movie movie = new Movie()
                    {
                        ID = xMovie.Element("id").Value,
                        Title = xMovie.Element("title").Value,
                        ShortOverview = xMovie.Element("short_overview") != null ? xMovie.Element("short_overview").Value : ""
                    };
                    XElement poster = xMovie.Elements("poster").Where(p => p.Attribute("size") != null && p.Attribute("size").Value == "thumb").FirstOrDefault();
                    if (poster != null)
                        movie.ThumbnailUrl = poster.Value;
                    poster = xMovie.Elements("poster").Where(p => p.Attribute("size") != null && p.Attribute("size").Value == "mid").FirstOrDefault();
                    if (poster != null)
                        movie.PosterUrl = poster.Value;
                    poster = xMovie.Elements("backdrop").Where(p => p.Attribute("size") != null && p.Attribute("size").Value == "original").FirstOrDefault();
                    if (poster != null)
                        movie.BackgroundUrl = poster.Value;
                    movies.Add(movie);
                }
                catch { }
            }
            return movies;
        }

        public List<Movie> GetMovieInfo(Movie movie, ImageType imageType)
        {
            XDocument xdoc = XDocument.Load(string.Format(MovieInfoString, movie.ID));
            List<Movie> movies = new List<Movie>();

            List<XElement> images;
            if (imageType == ImageType.poster)
            {
                images = xdoc.Descendants("poster").ToList();
                foreach (XElement image in images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "original"))
                {
                    string temp = image.Value;
                    temp = temp.Replace(System.IO.Path.GetExtension(temp), "");

                    try
                    {
                        Movie tmovie = new Movie() { ID = movie.ID, Title = movie.Title, ShortOverview = movie.ShortOverview };
                        XElement thumb = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "cover" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (thumb == null)
                            thumb = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "mid" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (thumb != null)
                            tmovie.ThumbnailUrl = thumb.Value;

                        XElement full = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "mid" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (full == null)
                            full = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "cover" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (full != null)
                            tmovie.ImageUrl = full.Value;
                        else
                            continue;

                        movies.Add(tmovie);
                    }
                    catch { }
                }
            }
            else if (imageType == ImageType.background)
            {
                images = xdoc.Descendants("backdrop").ToList();
                foreach (XElement image in images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "original"))
                {
                    string temp = image.Value;
                    temp = temp.Replace(System.IO.Path.GetExtension(temp), "");

                    try
                    {
                        Movie tmovie = new Movie() { ID = movie.ID, Title = movie.Title, ShortOverview = movie.ShortOverview };
                        XElement thumb = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "mid" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (thumb == null)
                            thumb = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "original" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (thumb != null)
                            tmovie.ThumbnailUrl = thumb.Value;

                        XElement full = images.Where(i => i.Attribute("size") != null && i.Attribute("size").Value == "original" && i.Value.StartsWith(temp)).FirstOrDefault();
                        if (full != null)
                            tmovie.ImageUrl = full.Value;
                        else
                            continue;

                        movies.Add(tmovie);
                    }
                    catch { }
                }
            }

            return movies;
        }

        public Movie GetDefualtImages(string title, string imdb)
        {
            List<Movie> movies = SearchByIMDB(imdb);
            if (movies.Count == 1)
                return movies[0];
            movies = SearchByTitle(title);
            if (movies.Count == 1)
                return movies[0];
            return null;
        } 
        #endregion
    }

    public static class LinqEntitySerializationHelper<T>
    {
        /// <summary>
        /// Used to Serialize and Deserialize objects, usually used for Linq entities that need to be serialized
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>entity</returns>
        public static T SerializeAndDeserialize(T entity)
        {
            //Have to simulate serializing as if it came from a web page or a service call
            using (MemoryStream ms = new MemoryStream())
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                serializer.WriteObject(ms, entity);
                ms.Seek(0, 0);
                return (T)serializer.ReadObject(ms);
            }
        }
    }
}
